/**
 * fulihui.com Inc.
 * Copyright (c) 2015-2016 All Rights Reserved.
 */
package com.fulihui.integral.core.dal.repository.impl;

import com.fulihui.integral.core.common.AppConstConfig;
import com.fulihui.integral.core.common.ISBaseErrors;
import com.fulihui.integral.core.dal.dataobj.*;
import com.fulihui.integral.core.dal.mapper.ISAccountDetailMapper;
import com.fulihui.integral.core.dal.mapper.ISAccountInfoMapper;
import com.fulihui.integral.core.dal.mapper.ISAccountInfoTempMapper;
import com.fulihui.integral.core.dal.repository.ISUserBehaviorRepository;
import com.fulihui.integral.core.dal.repository.result.TransactionResult;
import com.fulihui.integral.core.enums.ISDoType;
import com.fulihui.integral.core.request.ISUserBehaviorReq;
import com.fulihui.integral.core.request.duiba.DuibaLoginReq;
import com.fulihui.integral.core.result.duiba.DuibaLoginResult;
import com.fulihui.integral.core.util.AssembleTool;
import com.fulihui.integral.core.util.ClassFieldsUtil;
import com.fulihui.integral.core.util.SignTool;
import org.apache.commons.lang3.StringUtils;
import org.near.servicesupport.error.InvokeServiceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.CollectionUtils;

import javax.inject.Inject;
import java.util.Date;
import java.util.List;
import java.util.Map;

import static com.fulihui.integral.core.dal.repository.result.TransactionResultBuilder.buildFail;
import static com.fulihui.integral.core.dal.repository.result.TransactionResultBuilder.buildSuc;

/**
 * 积分账户操作
 *
 * @author yunfeng.li
 * @version $Id: v 0.1 2017年04月13日 13:07 yunfeng.li Exp $
 */
@Repository
public class ISUserBehaviorRepositoryImpl implements ISUserBehaviorRepository {

    private Logger logger = LoggerFactory.getLogger(ISUserBehaviorRepository.class);

    @Autowired
    private TransactionTemplate transactionTemplate;

    @Autowired
    private ISAccountInfoMapper iSAccountInfoMapper;

    @Autowired
    private ISAccountInfoTempMapper iSAccountInfoTempMapper;

    @Autowired
    private ISAccountDetailMapper iSAccountDetailMapper;

    /**
     * 重试次数
     */
    private int RETRY = 4;

    /**
     * 超时时间
     */
    private int TIMEOUT = 100000;

    @Autowired
    private AppConstConfig appConstConfig;

    /**
     * 添加或者减少用户积分
     * <p>乐观锁并发控制<p/>
     *
     * @param req
     * @param doType
     * @return
     */
    @Override
    public TransactionResult addOrSubAmount(ISUserBehaviorReq req, String doType) {
        TransactionResult tResult = new TransactionResult();
        int retryCount = 0;
        long timeout = System.currentTimeMillis();
        while (retryCount < RETRY && System.currentTimeMillis() - timeout < TIMEOUT) {
            retryCount++;
            tResult = this.excute(req, doType, tResult);
            if (tResult.isSuccess()) {
                return tResult;
                //如果不是版本更新冲突问题，不再重试，直接跳出
            } else if (tResult.getErrorCode() != ISBaseErrors.NO_DATA_MODIFY_INFO.getErrcode()) {
                return tResult;
            }
        }

        return tResult;
    }



    /**
     * 执行
     *
     * @param req
     * @param doType
     * @param tResult
     * @return
     */
    private TransactionResult excute(ISUserBehaviorReq req, String doType,
                                     TransactionResult tResult) {
        String userId = req.getUserId();
        String isAccountType = req.getIsAccountType();
        Long amount = req.getAmount();
        boolean result = transactionTemplate.execute((TransactionStatus status) -> {
            try {
                // 查询该账户积分余额
                ISAccountInfoExample selectExample = new ISAccountInfoExample();
                ISAccountInfoExample.Criteria criteria = selectExample.createCriteria();
                criteria.andUserIdEqualTo(userId);
                criteria.andIsAccountTypeEqualTo(isAccountType);
                List<ISAccountInfo> isAccountInfos = iSAccountInfoMapper.selectByExample(selectExample);
                if (!CollectionUtils.isEmpty(isAccountInfos)) {
                    // 原余额加当前余额，修改账户积分余额
                    ISAccountInfo isAccountInfo = isAccountInfos.get(0);
                    updateAccountInfo(isAccountInfo, amount, doType);
                } else {
                    // 没有用户记录，直接插入
                    insertAccountInfo(isAccountType, amount, userId);
                }
                //记录交易流水号
                insertAccountDetail(req, doType);
            } catch (InvokeServiceException ex) {
                status.setRollbackOnly();
                buildFail(tResult, ex.getErrcode(), ex.getMessage());
                return false;
            } catch (Exception e) {
                status.setRollbackOnly();
                buildFail(tResult, 500, e.getMessage());
                return false;
            }
            copyTemp(req.getIsAccountType(), doType, req.getUserId(), req.getAmount());
            return true;
        });
        if (result) {
            buildSuc(tResult);
            return tResult;
        }
        return tResult;
    }

    /**
     * 添加积分账户
     *
     * @param isAccountType
     * @param amount
     * @param userId
     */
    private void insertAccountInfo(String isAccountType, Long amount, String userId) {
        ISAccountInfo upRecord = new ISAccountInfo();
        upRecord.setUserId(userId);
        upRecord.setIsAccountType(isAccountType);
        upRecord.setAmount(amount);
        upRecord.setAddAmount(amount);
        upRecord.setVersion(1L);
        upRecord.setGmtCreate(new Date());
        upRecord.setGmtModifed(new Date());
        int target = iSAccountInfoMapper.insertSelective(upRecord);
        if (target != 1) {
            throw new InvokeServiceException(ISBaseErrors.NO_DATA_INSERT_INFO);
        }
    }

    /**
     * 修改积分账户
     *
     * @param isAccountInfo
     * @param amount
     */
    private void updateAccountInfo(ISAccountInfo isAccountInfo, Long amount, String doType) {
        ISAccountInfo upRecord = new ISAccountInfo();
        Long oldAmount = isAccountInfo.getAmount();
        if (StringUtils.equals(doType, ISDoType.ADD.getCode())) {
            Long addAmount = oldAmount + amount;
            upRecord.setAmount(addAmount);
            upRecord.setAddAmount(isAccountInfo.getAddAmount() + amount);
        } else if (StringUtils.equals(doType, ISDoType.SUB.getCode())) {
            if (amount <= oldAmount) {
                Long subAmount = oldAmount - amount;
                upRecord.setAmount(subAmount);
            } else {
                logger.error("积分余额不足,userId={}", isAccountInfo.getUserId());
                throw new InvokeServiceException(ISBaseErrors.USER_ACCOUNT_JIFEN_INSUFFICIENT);
            }
        } else {
            logger.error("请求非法，没有指定操作类型或无法识别的操作类型-{}", doType);
            throw new InvokeServiceException(ISBaseErrors.ILLEGAL_OPERATION_TYPE);
        }

        // 乐观锁，每次更新版本号+1,修改之后，如果表版本号大于当前版本号，则修改失败，触发重试机制
        upRecord.setVersion(isAccountInfo.getVersion() + 1);
        upRecord.setGmtModifed(new Date());

        ISAccountInfoExample upExample = new ISAccountInfoExample();
        ISAccountInfoExample.Criteria upCriteria = upExample.createCriteria();
        upCriteria.andIsAccountTypeEqualTo(isAccountInfo.getIsAccountType());
        upCriteria.andVersionEqualTo(isAccountInfo.getVersion());
        upCriteria.andUserIdEqualTo(isAccountInfo.getUserId());
        int target = iSAccountInfoMapper.updateByExampleSelective(upRecord, upExample);
        if (target != 1) {
            // 版本冲突！
            throw new InvokeServiceException(ISBaseErrors.NO_DATA_MODIFY_INFO);
        }
    }

    /**
     * 添加积分流水
     *
     * @param req
     * @param doType
     */
    private void insertAccountDetail(ISUserBehaviorReq req, String doType) {
        ISAccountDetail record = new ISAccountDetail();
        record.setAmount(req.getAmount());
        record.setChannel(req.getIsChannelType());
        record.setUserId(req.getUserId());
        record.setIsAccountType(req.getIsAccountType());
        record.setOpType(doType);
        record.setMemo(req.getMemo());
        record.setOutTradeNo(req.getOutTradeNo());
        record.setGmtCreate(new Date());
        record.setGmtModifed(new Date());
        logger.info("record:{}",record.toString());
        int target = iSAccountDetailMapper.insertSelective(record);
        if (target != 1) {
            throw new InvokeServiceException(ISBaseErrors.NO_DATA_INSERT_DETAIL);
        }
    }

    /**
     * 全量记录备份表
     *
     * @param isAccountType
     * @param doType
     * @param userId
     * @param amount
     */
    private void copyTemp(String isAccountType, String doType, String userId, Long amount) {
        try {
            ISAccountInfoTempExample example = new ISAccountInfoTempExample();
            ISAccountInfoTempExample.Criteria criteria = example.createCriteria();
            criteria.andUserIdEqualTo(userId);
            criteria.andIsAccountTypeEqualTo(isAccountType);
            List<ISAccountInfoTemp> isAccountInfoTemps = iSAccountInfoTempMapper.selectByExample(example);
            if (CollectionUtils.isEmpty(isAccountInfoTemps)) {
                ISAccountInfoTemp record = new ISAccountInfoTemp();
                record.setAmount(amount);
                record.setAddAmount(amount);
                record.setIsAccountType(isAccountType);
                record.setUserId(userId);
                record.setGmtCreate(new Date());
                record.setGmtModifed(new Date());
                iSAccountInfoTempMapper.insert(record);
            } else {
                ISAccountInfoTemp infoTemp = isAccountInfoTemps.get(0);
                Long oldAmount = infoTemp.getAmount();

                ISAccountInfoTemp updateInfo = new ISAccountInfoTemp();
                updateInfo.setUserId(userId);
                updateInfo.setId(infoTemp.getId());
                updateInfo.setIsAccountType(isAccountType);
                if (StringUtils.equals(doType, ISDoType.ADD.getCode())) {
                    Long addAmount = oldAmount + amount;
                    updateInfo.setAmount(addAmount);
                    updateInfo.setAddAmount(infoTemp.getAddAmount() + amount);
                } else if (StringUtils.equals(doType, ISDoType.SUB.getCode())) {
                    if (amount < oldAmount) {
                        Long subAmount = oldAmount - amount;
                        updateInfo.setAmount(subAmount);
                    }
                }
                updateInfo.setGmtModifed(new Date());
                iSAccountInfoTempMapper.updateByPrimaryKeySelective(updateInfo);
            }
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("复制积分记录到全量备份表出现异常", e);
        }
    }


    @Override
    public DuibaLoginResult redirectLoginUrl(DuibaLoginReq request,DuibaLoginResult result) {
        request.setAppKey(appConstConfig.getDuibaAppKey());
        request.setAppSecret(appConstConfig.getDuibaAppSecret());
        Map<String, String> stringObjectMap = ClassFieldsUtil.obj2StrValMap(request);
        stringObjectMap.put("timestamp",System.currentTimeMillis()+"");
        String sign= SignTool.sign(stringObjectMap);
        stringObjectMap.put("sign", sign);
        String url = AssembleTool
                .assembleUrl(appConstConfig.getDuibaLoginUrl(), stringObjectMap, "appSecret");
        result.setUrl(url);
        return result;
    }

}
